#include <my_header.h>
typedef struct goods_s{
    int name;
    struct goods_s* next;
}goods_t;

typedef struct
{
    goods_t * head;
    goods_t * tail;
    int size;
}Queue_t;

Queue_t * insert_head(Queue_t * queue);
void print_goods(Queue_t *queue);
goods_t *remove_tail(Queue_t *queue);

typedef struct share_value_s{
    Queue_t *queue;
    pthread_mutex_t mutex;
    pthread_cond_t cond_producer;
    pthread_cond_t cond_consumer;
}share_value_t;

void * threadFunc_producer_1(void * arg);
void * threadFunc_producer_2(void * arg);
void * threadFunc_producer_3(void * arg);
void * threadFunc_consumer_1(void * arg);
void * threadFunc_consumer_2(void * arg);
/* usage */
int main(int argc, char *argv[])
{
    ARGS_CHECK(argc,1);
    share_value_t share_value;

    share_value.queue = (Queue_t *)calloc(1,sizeof(Queue_t));
    share_value.queue->size = 0;

    srand(time(NULL));
    for(int i = 0 ; i < 8 ; i++)
    {
        insert_head(share_value.queue);
    }
    printf("Initial goods: ");
    print_goods(share_value.queue);

    pthread_t producer[3];
    pthread_t consumer[2];
    pthread_mutex_init(&share_value.mutex,NULL);
    pthread_cond_init(&share_value.cond_consumer,NULL);
    pthread_cond_init(&share_value.cond_producer,NULL);

    pthread_create(&producer[0],NULL,threadFunc_producer_1,&share_value);
    pthread_create(&producer[1],NULL,threadFunc_producer_2,&share_value);
    pthread_create(&producer[2],NULL,threadFunc_producer_3,&share_value);

    pthread_create(&consumer[0],NULL,threadFunc_consumer_1,&share_value);
    pthread_create(&consumer[1],NULL,threadFunc_consumer_2,&share_value);


    for(int i = 0 ; i< 2;i++)
    {
        pthread_join(producer[i],NULL);
        pthread_join(consumer[i],NULL);
    }
    pthread_join(producer[2],NULL);
    return 0;
}

Queue_t * insert_head(Queue_t * queue)
{
    goods_t * new_goods = (goods_t *)malloc(sizeof(goods_t));

    new_goods->name = rand()%100 + 1;

    new_goods->next = queue->head;
    queue->head = new_goods;

    if(queue->tail == NULL)
    {
        queue->tail = new_goods;
    }
    queue->size ++;

    return queue;
}

goods_t *remove_tail(Queue_t *queue)
{
    if(queue->head == NULL)
    {
        return NULL;
    }
    goods_t *prve = NULL;
    goods_t *current = queue->head;

    while(current->next != NULL)
    {
        prve = current;
        current = current->next;
    }

    goods_t *removed = current;
    if(prve != NULL)
    {
        prve ->next = NULL;
        queue->tail = prve;
    }
    else
    {
        queue->head = queue->tail = NULL;
    }
    queue->size--;
    return removed;
}

void print_goods(Queue_t *queue)
{
    goods_t *cur_goods = queue->head;
    while(cur_goods != NULL)
    {
        printf("%d",cur_goods->name);
        if(cur_goods->next != NULL)
        {
            printf("->");
        }
        cur_goods = cur_goods->next;
    }
    printf("\n");
}

void * threadFunc_producer_1(void * arg)
{
    share_value_t *pshare_value = (share_value_t *)arg;
    while(1)
    {
        sleep(3);
        pthread_mutex_lock(&pshare_value->mutex);
        while(pshare_value->queue->size >= 10)
        {
            pthread_cond_wait(&pshare_value->cond_producer,
                              &pshare_value->mutex);
        }
        insert_head(pshare_value->queue);
        printf("Produce1 prodeced: %d. Queue size: %d\n",
               pshare_value->queue->head->name,
               pshare_value->queue->size);
        print_goods(pshare_value->queue);

        pthread_cond_signal(&pshare_value->cond_consumer);
        pthread_mutex_unlock(&pshare_value->mutex);
    }
    return NULL;
}

void * threadFunc_producer_2(void * arg)
{
    share_value_t *pshare_value = (share_value_t *)arg;
    while(1)
    {
        sleep(3);
        pthread_mutex_lock(&pshare_value->mutex);
        while(pshare_value->queue->size >= 10)
        {
            pthread_cond_wait(&pshare_value->cond_producer,
                              &pshare_value->mutex);
        }
        insert_head(pshare_value->queue);
        printf("Produce2 prodeced: %d. Queue size: %d\n",
               pshare_value->queue->head->name,
               pshare_value->queue->size);
        print_goods(pshare_value->queue);

        pthread_cond_signal(&pshare_value->cond_consumer);
        pthread_mutex_unlock(&pshare_value->mutex);
    }
}
void * threadFunc_producer_3(void * arg)
{
    share_value_t *pshare_value = (share_value_t *)arg;
    while(1)
    {
        sleep(3);
        pthread_mutex_lock(&pshare_value->mutex);
        while(pshare_value->queue->size >= 10)
        {
            pthread_cond_wait(&pshare_value->cond_producer,
                              &pshare_value->mutex);
        }
        insert_head(pshare_value->queue);
        printf("Produce3 prodeced: %d. Queue size: %d\n",
               pshare_value->queue->head->name,
               pshare_value->queue->size);
        print_goods(pshare_value->queue);

        pthread_cond_signal(&pshare_value->cond_consumer);
        pthread_mutex_unlock(&pshare_value->mutex);
    }


}

void * threadFunc_consumer_1(void * arg)
{
    share_value_t *pshare_value = (share_value_t *)arg;
    sleep(5);
    while(1)
    {
        sleep(1);
        pthread_mutex_init(&pshare_value->mutex,NULL);
        while(pshare_value->queue->size <= 0)
        {
            pthread_cond_wait(&pshare_value->cond_producer,
                              &pshare_value->mutex);
        }
        goods_t *consumed = remove_tail(pshare_value->queue);
        printf("Consumer1 consumed: %d . Queue size: %d\n",
               consumed->name,pshare_value->queue->size);
        print_goods(pshare_value->queue);
        free(consumed);

        pthread_cond_signal(&pshare_value->cond_producer);
        pthread_mutex_unlock(&pshare_value->mutex);
    }
    return NULL;
}

void * threadFunc_consumer_2(void * arg)
{
    share_value_t *pshare_value = (share_value_t *)arg;
    sleep(5);
    while(1)
    {
        sleep(1);
        pthread_mutex_init(&pshare_value->mutex,NULL);
        while(pshare_value->queue->size <= 0)
        {
            pthread_cond_wait(&pshare_value->cond_producer,
                              &pshare_value->mutex);
        }
        goods_t *consumed = remove_tail(pshare_value->queue);
        printf("Consumer2 consumed: %d . Queue size: %d\n",
               consumed->name,pshare_value->queue->size);
        print_goods(pshare_value->queue);
        free(consumed);

        pthread_cond_signal(&pshare_value->cond_producer);
        pthread_mutex_unlock(&pshare_value->mutex);
    }
    return NULL;
}
